package com.jweb.framework.core.impl;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.transaction.support.TransactionOperations;

import com.jweb.framework.core.Action;
import com.jweb.framework.core.ActionHolder;
import com.jweb.framework.core.Context;
import com.jweb.framework.core.CoreException;
import com.jweb.framework.core.CoreRuntimeException;
import com.jweb.framework.core.Executable;
import com.jweb.framework.core.service.annotation.JTransactional;
import com.jweb.framework.core.support.TRACER;
import com.jweb.framework.core.support.Utils;


public class DefaultActionHolder implements ActionHolder , ApplicationContextAware {
	
	private Action jservice ;
	private String name ;
	private String[] operations ;
	private boolean throwNoSuchMethodException ;
	private ApplicationContext applicationContext ;
	
	public DefaultActionHolder(boolean flag , String name , boolean tse){
		this((flag ? PLACE_HOLDER_ACTION : EMPTY_ACTION) , name , tse);
	}
	
	public DefaultActionHolder(Action action , String name , boolean tse){
		this.jservice = action ;
		this.name = name ;
		this.throwNoSuchMethodException = tse ;
	}

	public ApplicationContext getApplicationContext() {
		return applicationContext;
	}

	public void setJkservice(Action action){
		this.jservice = action ;
	}

	public void setName(String name) {
		this.name = name;
	}

	public void execute(Context context) throws CoreException,
			CoreRuntimeException {
		
		Object obj = null ;
		try {
			
			if(this.operations != null){
				TRACER.trace("Starting jservice '{}' with operation '{}' , class:{}" , new Object[]{this.name , Arrays.asList(this.operations) , jservice.getClass().getName()});
				String as[] ;
				int j = (as=operations).length ;
				for(int i= 0 ; i < j ; i++){
					String s = as[i];
					execute(s , context);
				}
			}
			else if(jservice instanceof Executable){
				TRACER.trace("Starting executable jservice '{}' , class:{}" , new String[]{this.name , jservice.getClass().getName()});
				execute("execute" , context) ;
			}
			else {
				throw new CoreRuntimeException("JAVACO0001" , "no_operation_definied");
			}
			
		} catch (CoreRuntimeException e) {
			throw e ;
		}
	}
	
	private void execute(String optName , Context context) throws CoreException , CoreRuntimeException {
		
		Method method = null ;
		
		try {
			method = jservice.getClass().getMethod(optName, new Class[]{Context.class});
			
		} catch (NoSuchMethodException e) {
			if(throwNoSuchMethodException){
				throw new CoreRuntimeException("JAVACO0002" , "no_method_for_operation" , e);
			}
			else {
				TRACER.trace("no method found : 'void {}(Context) throws CoreException ' , just skip it" , new String[]{optName});
				return  ;
			}
		}
		
		try {
			
			JTransactional tx = (JTransactional)method.getAnnotation(JTransactional.class);
			if(tx == null){
				method.invoke(jservice , new Object[]{context}) ;
			}
			else {
				TransactionOperations transactionOperations = null ;
				String txName = tx.value();
				if(txName.length() == 0){
					transactionOperations = (TransactionOperations) this.applicationContext.getBean("txTemplate");
				}
				else {
					transactionOperations = this.applicationContext.getBean(txName , TransactionOperations.class);
				}
				Utils.runInTx(transactionOperations, jservice , method, context);
			}
			
		} catch (InvocationTargetException e) {
			Throwable t = e.getTargetException();
			exceptionHandler(context , t);
		} catch (Throwable t){
			exceptionHandler(context , t);
		}
	}
	
	private void exceptionHandler(Context context , Throwable throwable) throws CoreException , CoreRuntimeException {
		
		if(throwable instanceof Error){
			throw (Error)throwable ;
		}
		if(throwable instanceof CoreRuntimeException){
			throw (CoreRuntimeException)throwable ;
		}
		if(throwable instanceof CoreException){
			context.setException((CoreException)throwable);
			throw (CoreException)throwable ;
		}
		else {
			CoreException ce = new CoreException("JAVACO007" , throwable);
			ce.setWrapped(true);
			context.setException(ce);
			throw ce ;
		}
		
	}
	
	public Action getJservice(){
		return this.jservice ;
	}

	public String getName() {
		return this.name ;
	}

	public String[] getOperations() {
		return this.operations ;
	}

	public boolean isThrowNoSuchMethodException() {
		return this.throwNoSuchMethodException ;
	}

	public void setOperations(String[] operations) {
		this.operations = operations ;
	}

	public void setThrowNoSuchMethodException(boolean paramBoolean) {
		this.throwNoSuchMethodException = paramBoolean ;
	}

	public void setApplicationContext(ApplicationContext arg0)
			throws BeansException {
		this.applicationContext = arg0;
	}

}
